#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <set>
#include <cstdio>

#include "Log.hpp"

using namespace std;

class Command
{
private:
    void LoadConf(const string &filepath)
    {
        ifstream in(filepath, ios::in);
        if (!in.is_open())
        {
            LOG(FATAL, "open file error");
            return;
        }

        string line;
        while (getline(in, line))
        {
            if (line.empty())
                continue;
            LOG(DEBUG, "load command [%s] success\n", line.c_str());
            _safe_cmd.insert(line);
        }

        in.close();
    }

public:
    Command(const string &cond_path)
        : _cond_path(cond_path)
    {
        LoadConf(_cond_path); // 构造时加载配置文件
    }

    bool SafeCheck(const string &cmd)
    {
        set<string>::iterator pos = _safe_cmd.find(cmd);
        if (pos == _safe_cmd.end())
        {
            return false;
        }
        return true;
    }
    string Excute(const string &cmd)
    {
        // 思路:
        // 父进程以读写方式打开管道文件,fork创建子进程,子进程关闭写端,父进程关闭读端
        // 父进程将获取的指令通过管道的写端写入内核级文件缓冲区,子进程从管道文件的内核级缓冲区中读取指令
        // 子进程读取到指令之后执行程序替换,执行指令,将本该输出到标准输出重定向到管道文件,父进程便可得到执行结果
        string result;
        if (SafeCheck(cmd))
        {
            FILE *pf = popen(cmd.c_str(), "r");
            if(pf==nullptr)
            {
                return "excute failed";
            }
            char buffer[1024];
            while(fgets(buffer,sizeof(buffer),pf)!=NULL)
            {
                result += buffer;
            }
            pclose(pf);
        }
        else
        {
            return "坏蛋";
        }
        return result;
    }
    ~Command()
    {
    }

private:
    set<string> _safe_cmd; // 保存远程输入的安全指令
    string _cond_path;     // 安全指令配置文件路径
};